﻿namespace Behemoth.SharePoint.TimerJobs {

    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Reflection;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    using Behemoth.Configuration;
    using Behemoth.SharePoint.Configuration;
    using Behemoth.SharePoint.Logging;
    using BSLog=Behemoth.SharePoint.Logging;

    public class WebBindableTimerJobReceiver : SPFeatureReceiver {

        protected ULSTraceLogging ulsLog;

        public WebBindableTimerJobReceiver(string logProduct, string logCategory)
            : base() {
            ulsLog = new ULSTraceLogging();
            ulsLog.DefaultProduct = logProduct;
            ulsLog.DefaultCategory = logCategory;
        }

        protected virtual string GetTimerJobName() {
            throw new NotImplementedException("Developers should override this method.");
        }

        protected virtual SPFeaturePropertyReaderBase EnsureProperties(SPFeatureReceiverProperties properties) {
            throw new NotImplementedException("Developers should override this method.");
        }

        protected virtual WebBindableTimerJob CreateJobDefinition(SPWebApplication app, bool doUpdate) {
            throw new NotImplementedException("Developers should override this method.");
            /*
            string jobName = this.GetTimerJobName();
            string jobTitle = jobName;
            string schedule = string.Empty; // TODO set a default schedule
            SPJobLockType lockType = SPJobLockType.None;
            WebBindableTimerJob job = new WebBindableTimerJob(jobName, jobTitle, web.Site.WebApplication, lockType);
            if (!string.IsNullOrEmpty(schedule))
                job.Schedule = SPSchedule.FromString(schedule);
            if (doUpdate)
                job.Update();
            return job;
             */
        }

        /*
         * Developerws will need to implement something like this...
         * 
        private VRGenerationTimerJobProperties props;
        private VRGenerationTimerJobProperties TypedProperties {
            get { return props; }
        }

        protected override SPFeaturePropertyReaderBase EnsureProperties(SPFeatureReceiverProperties properties) {
            ulsLog.Write("Invoking EnsureProperties.");
            if (props == null)
                props = new VRGenerationTimerJobProperties(properties);
            else if (props.InitStatus != Status.Initialized)
                throw new Exception("Property reader is not initialized. Can't continue.");
            ulsLog.Write("Leaving EnsureProperties.");
            return props;
        }
         */

        protected void BindWeb(SPWeb web) {
            if (web == null)
                throw new ArgumentNullException("Expecting a valid SPWeb object instance.", "web");
            string jobName = this.GetTimerJobName();
            // get the job def
            WebBindableTimerJob job = WebBindableTimerJob.GetJobDefinition(web, jobName, false) as WebBindableTimerJob;
            // if the job does not exist, create it
            /* commented out because we now rely on a feature activation event to create the job
            if (job == null)
                job = this.CreateJobDefinition(web, true);
             */
            if (job == null)
                throw new ArgumentNullException("Uh oh! Despite best attempt to get a WebBindableTimerJob, the return value was still null. ", "job");
            // attempt to add the web and update
            WebBindableTimerJob.BindWeb(job, web);
        }
        protected bool UnbindWeb(SPWeb web) {
            if (web == null)
                throw new ArgumentNullException("Expecting a valid SPWeb object instance.", "web");
            string jobName = this.GetTimerJobName();
            // get the job def
            WebBindableTimerJob job = WebBindableTimerJob.GetJobDefinition(web, jobName, false) as WebBindableTimerJob;
            // remove the web and update, or if it was the last one, delete the job
            // done this way to exit gracefully if activated feature ends up in a "weird" state where teh job is gone
            if (job == null)
                return false;
            return job.UnbindWeb(web, false);
        }

        public override void FeatureActivated(SPFeatureReceiverProperties properties) {
            ulsLog.Write(string.Format("Entering FeatureActivating for {0}. **!!**", this.GetType().Name));
            string exMsg = "get properties";
            try {
                SPFeaturePropertyReaderBase typedProps = EnsureProperties(properties);
                if (typedProps.Web == null) {
                    // when receiver is bound at the web app level, create the timer job
                    exMsg = "uninstall timer job";
                    SPWebApplication app = typedProps.WebApplication;
                    string jobName = this.GetTimerJobName();
                    WebBindableTimerJob job = WebBindableTimerJob.GetJobDefinition(app, jobName, false) as WebBindableTimerJob;
                    // only create the job if it does not exist
                    if (job == null)
                        job = CreateJobDefinition(app, true);
                } else {
                    // when receiver is bound at the web level, bind the web to the timer job
                    exMsg = "bind web to timer job";
                    BindWeb(typedProps.Web);
                }
            } catch (Exception ex) {
                ulsLog.Write(string.Format(
                    "Failed to {0} at FeatureActivated for {1}.",
                    exMsg,
                    this.GetType().Name
                ), BSLog.TraceSeverity.Exception);
                ulsLog.Write(ex);
                throw ex;
            }
            ulsLog.Write(string.Format("Leaving FeatureActivating for {0}.", this.GetType().Name));
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
            ulsLog.Write(string.Format("Entering FeatureDeactivating for {0}. **!!**", this.GetType().Name));
            string exMsg = "get properties";
            try {
                SPFeaturePropertyReaderBase typedProps = EnsureProperties(properties);
                if (typedProps.Web == null) {
                    // when receiver is bound at the web app level, delete the timer job
                    exMsg = "uninstall timer job";
                    ulsLog.Write(string.Format("Trying {0}", exMsg), BSLog.TraceSeverity.Verbose);
                    SPWebApplication app = typedProps.WebApplication;
                    string jobName = this.GetTimerJobName();
                    ulsLog.Write(string.Format("Job name = '{0}'.", jobName), BSLog.TraceSeverity.Verbose);
                    WebBindableTimerJob job = WebBindableTimerJob.GetJobDefinition(app, jobName, false) as WebBindableTimerJob;
                    ulsLog.Write(string.Format("Got job definition. job not null = '{0}'.", job != null), BSLog.TraceSeverity.Verbose);
                    if (job != null) {
                        job.Uncache();
                        job.Delete();
                        job.Unprovision();
                    }
                    ulsLog.Write("Done massive delete and unprovision operation.", BSLog.TraceSeverity.Verbose);
                } else {
                    // when receiver is bound at the web level, unbind the web from the timer job
                    exMsg = "unbind web from timer job";
                    UnbindWeb(typedProps.Web);
                }
            } catch (Exception ex) {
                ulsLog.Write(string.Format(
                    "Failed to {0} at FeatureDeactivating for {1}.",
                    exMsg,
                    this.GetType().Name    
                ), BSLog.TraceSeverity.Exception);
                ulsLog.Write(ex);
                throw ex;
            }
            ulsLog.Write(string.Format("Leaving FeatureDeactivating for {0}.", this.GetType().Name));
        }

        public override void FeatureInstalled(SPFeatureReceiverProperties properties) {
        /*    ulsLog.Write("Entering FeatureInstalled.");
                try {
                    EnsureProperties(properties);
                } catch (Exception ex) {
                    ulsLog.Write("Failed to do nothing in FeatureInstalled for WebBindableTimerJob.");
                    ulsLog.Write(ex);
                }
            ulsLog.Write("Leaving FeatureInstalled.");
         */
        }

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {
        /*
         ulsLog.Write("Entering FeatureUninstalling.");
              try {
                   SPFeaturePropertyReaderBase typedProps = EnsureProperties(properties);
                   SPWebApplication app = typedProps.WebApplication;
                   string jobName = this.GetTimerJobName();
                   // get the job def
                   WebBindableTimerJob job = WebBindableTimerJob.GetJobDefinition(app, jobName, false) as WebBindableTimerJob;
                   // remove the web and update, or if it was the last one, delete the job
                   if (job != null)
                       job.Delete();
               } catch (Exception ex) {
                   ulsLog.Write("Failed to uninstall timer job on FeatureUninstalling for WebBindableTimerJob.");
                   ulsLog.Write(ex);
               }
           ulsLog.Write("Leaving FeatureUninstalling.");
        */
        }

    } // class
} // namespace
